package com.framework.core.rest;

import android.support.annotation.Nullable;

import com.framework.core.exception.AppException;
import com.framework.core.exception.NetworkErrorException;
import com.framework.core.exception.NetworkTimeOutException;
import com.framework.core.exception.SystemException;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import rx.Observable;
import rx.Scheduler;
import rx.functions.Func1;

/**
 * @author shenmengchao
 * @version 1.0.0
 * @date 2017/7/18
 * @description
 */

public class RxTransformErrorCallAdapterFactory extends CallAdapter.Factory {

    private final RxJavaCallAdapterFactory original;

    private RxTransformErrorCallAdapterFactory() {
        original = RxJavaCallAdapterFactory.create();
    }

    private RxTransformErrorCallAdapterFactory(Scheduler scheduler) {
        original = RxJavaCallAdapterFactory.createWithScheduler(scheduler);
    }

    static CallAdapter.Factory create() {
        return new RxTransformErrorCallAdapterFactory();
    }

    static CallAdapter.Factory createWithScheduler(Scheduler scheduler) {
        return new RxTransformErrorCallAdapterFactory(scheduler);
    }

    @Nullable
    @Override
    public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        CallAdapter<?> adapter = original.get(returnType, annotations, retrofit);
        if (null == adapter) {
            return null;
        }
        return new RxCallAdapterWrapper(adapter);
    }

    private static class RxCallAdapterWrapper implements CallAdapter<Observable<?>> {

        private final CallAdapter<?> wrapped;

        public RxCallAdapterWrapper(CallAdapter<?> wrapped) {
            this.wrapped = wrapped;
        }

        @Override
        public Type responseType() {
            return wrapped.responseType();
        }

        @Override
        public <R> Observable<?> adapt(Call<R> call) {
            return ((Observable) wrapped.adapt(call)).onErrorResumeNext(new Func1<Throwable, Observable>() {
                @Override
                public Observable call(Throwable throwable) {
                    return Observable.error(transformException(throwable));
                }
            });
        }

        private Throwable transformException(Throwable throwable) {
            if (throwable instanceof IOException) {
                if (throwable instanceof UnknownHostException) {
                    return new NetworkErrorException();
                } else if (throwable instanceof ConnectException) {
                    return new NetworkErrorException();
                } else if (throwable instanceof SocketTimeoutException) {
                    return new NetworkTimeOutException();
                }
            }
            return throwable;
        }
    }
}
